功能
- 确保一系列的操作要么全部执行成功,要么全部执行失败
实现
事务消息流程
第一阶段:发送半消息(Half Message)
-
生产者 向 Broker 发送一条 半消息(对消费者不可见)。
-
Broker 将半消息存储在特殊 Topic(如
RMQ_SYS_TRANS_HALF_TOPIC
),而非目标 Topic。 -
关键点:半消息不会被消费者消费,但已持久化,确保后续可追溯。
第二阶段:执行本地事务
-
生产者执行 本地事务(如数据库操作),并根据事务结果决定提交或回滚消息。
-
提交:Broker 将半消息转移到目标 Topic,消费者可见。
-
回滚:Broker 直接删除半消息,流程终止。
事务状态回查(兜底机制)
-
若生产者未明确提交/回滚(如生产者在发送确认前宕机),Broker 会主动 回查事务状态:
-
Broker 定期扫描未完成的事务消息。
-
向生产者发起回查请求,询问该消息的最终状态。
-
生产者需实现
TransactionListener
,根据本地事务结果返回COMMIT
或ROLLBACK
。 -
Broker 根据回查结果提交或回滚消息。
-
Q&A
[!question] 如果生产者成功执行了本地事务,也就是提交了订单,那如果消费者未能成功消费消息的话,也就是未能减少库存,那么生产者会删除这个订单吗。这样的话,怎么体现事务的特性呢?
RocketMQ 事务消息主要解决的是生产者端的本地事务和消息发送的原子性,而不是端到端的事务。
让我来详细解释一下,并解答您的问题:
生产者不会删除已提交的订单。
关键点在于:RocketMQ 事务消息保证的是“生产者本地事务的成功提交”和“Commit 消息的成功发送”这两个操作的原子性。 一旦生产者成功提交了本地事务(创建订单),并发送了 Commit 消息,这个订单在生产者端就已经被认为是“已成功创建”的状态,不会因为消费者后续的消费行为而回滚或删除。
消费者消费失败与事务特性无关:
您所描述的“消费者未能成功消费消息,未能减少库存”这种情况,发生在 消费者端。 这属于 消费者业务逻辑执行失败 的范畴,不影响生产者已经成功提交的事务。
RocketMQ 事务消息的特性体现在以下方面:
- 生产者本地事务与消息发送的原子性: 保证了要么本地事务成功,消息才会被发送(并最终被消费者消费),要么本地事务失败,消息也不会被发送。 避免了“消息已发送,但本地事务失败”或者“本地事务成功,但消息未发送”的数据不一致情况。
- Half 消息的暂存与最终确认: 通过 Half 消息机制,Broker 暂存消息,等待生产者本地事务执行结果。最终根据生产者的 Commit 或 Rollback 命令来决定消息的命运。
- 事务状态回查机制: 即使生产者因为网络问题或崩溃导致未能及时发送 Commit/Rollback 命令,Broker 也会主动回查生产者,确保最终的事务状态能够被确定。
消费者消费失败的原因和处理:
消费者未能成功消费消息(例如,扣减库存失败)可能有多种原因:
- 库存不足: 下单时库存充足,但到消费者实际扣减库存时,库存可能已经被其他订单扣减完毕。
- 消费者系统故障: 消费者服务出现网络问题、数据库连接异常、业务逻辑错误等。
- 消息处理逻辑错误: 消费者端的扣减库存逻辑本身存在 bug。
对于消费者消费失败的情况,RocketMQ 的机制是消息重试,而不是生产者回滚。
- 消息重试机制: 当消费者返回 RECONSUME_LATER(稍后重试)或抛出异常时,RocketMQ 会对该消息进行重试投递。 默认情况下,消息会重试多次(例如,默认 16 次),每次重试的间隔时间会逐渐延长。
- 死信队列: 如果消息重试达到最大次数仍然消费失败,RocketMQ 会将该消息放入 死信队列 (Dead-Letter Queue, DLQ)。 进入死信队列的消息表示“永久消费失败”。
如何体现事务的特性?
虽然消费者端的失败不影响生产者已提交的事务,但事务的特性仍然体现在 数据最终一致性 的努力上:
- 消息最终被消费 (或进入死信队列): RocketMQ 的消息可靠性机制(包括重试)保证了 Commit 消息最终会被投递到消费者。 消费者有机会在重试过程中成功消费消息。
- 消费者端需要保证幂等性: 由于消息可能会被重复投递(重试机制),消费者端必须实现 消息处理的幂等性。 即使多次收到相同的消息,多次执行扣减库存操作,最终的结果也应该和执行一次的结果相同。 例如,可以根据订单 ID 来判断是否已经扣减过库存,如果已经扣减过则直接忽略。
- 监控和补偿机制: 对于进入死信队列的消息,需要人工介入处理。 例如,监控死信队列,发现有订单相关的消息进入死信队列,需要人工排查原因,进行数据补偿或人工处理订单。
总结:
RocketMQ 事务消息的核心目标是保证 生产者端操作 的原子性。 它并不能直接控制或回滚 消费者端操作。
当消费者消费失败时,RocketMQ 通过 消息重试 来提高消费者最终消费成功的概率,最终达到 数据最终一致性。 消费者端需要承担起 消息幂等性处理 和 失败处理 的责任。